<!--
    by: yuanbiao 2020年10月14日
    name: shake-spin 动效1：抖曳转圈
    notes:
-->
<template>
  <canvas ref="canvas" :width="width" :height="height"></canvas>
</template>

<script>
import {
  random,
  deepCopy,
  // polyfillRequestAnimationFrame,
  // polyfillCancelAnimationFrame
} from '@/script/utils.js'
export default {
  name: 'shake-spin',
  props: {
    options: {
      type: Object,
      default () {
        return {}
      }
    }
  },
  data() {
    return {
      circleListKeys: ['aux0', 'aux1', 'aux2', 'main'],
      circleList: {
        aux0: { controlPoints: [], points: [], offset: 0 },
        aux1: { controlPoints: [], points: [], offset: 3 },
        aux2: { controlPoints: [], points: [], offset: 6 },
        main: { controlPoints: [], points: [], offset: 9 }
      },
      width: 200,
      height: 200,
      volatility: 30,
      interval: 30,
      pointsCount: 10,
      imageUrl: require('@/images/img.jpg'),
      imgObject: null,
      auxColor: '#c5bfb5',
      mainColor: '#90857b',
      maskColor: 'rgba(255,255,255,0.5)',
      ctx: null,
      timer: null
    }
  },
  computed: {
    baseData () {
      let result = {}
      result.Rmax = Math.min(this.width / 2, this.height / 2)
      result.Rmin = Math.max(result.Rmax - this.volatility, 0)
      result.CenterX = this.width / 2
      result.CenterY = this.height / 2
      result.Vangle = Math.PI * 2 / 360
      result.Vline = 0.5
      return result
    }
  },
  watch: {
    options: {
      handler (n, o) {
        console.log(n, o)
        let whiteNames = [
          'width', 'height', 'volatility', 'interval', 'imageUrl',
          'pointsCount', 'auxColor', 'mainColor', 'maskColor'
        ]
        Object.keys(n).forEach(k => {
          if (whiteNames.includes(k)) this[k] = n[k]
          if (k === 'imageUrl') {
            this.loadImage(n[k])
          }
        })
      },
      deep: true,
      immediate: true
    }
  },
  mounted () {
    this.$nextTick(() => {
      this.init()
    })
  },
  beforeDestroy () {
    // window.cancelAnimationFrame(this.timer)
    clearTimeout(this.timer)
  },
  methods: {
    init () {
      this.ctx = this.$refs.canvas ? this.$refs.canvas.getContext('2d') : null
      for (let k in this.circleList) {
        this.circleList[k].controlPoints = this.createControlPoints(this.pointsCount, this.circleList[k].offset)
        this.circleList[k].points = this.createPointsByControlPoints(this.circleList[k].controlPoints)
      }
      // polyfillRequestAnimationFrame()
      // polyfillCancelAnimationFrame()
      this.loadImage(this.imageUrl)
      this.draw()
    },
    createControlPoints (pointsCount, offset = 0) {
      let result = []
      let step = 2 * Math.PI / pointsCount
      if (pointsCount < 3) return result
      for (let angle = 0; angle < 2 * Math.PI; angle += step) {
        let point = {}
        let _min = Math.max(this.baseData.Rmin - offset, 0)
        let _max = Math.max(this.baseData.Rmax - offset, 0)
        point.r = random(_min, _max, true)
        point.angle = angle
        point.x = this.baseData.CenterX + (Math.cos(angle) * point.r)
        point.y = this.baseData.CenterY + (Math.sin(angle) * point.r)
        result.push(point)
      }
      return result
    },
    createPointsByControlPoints (controlPoints) {
      let result = []
      let len = controlPoints.length
      if (len < 3) return result
      for (let i = 0; i <= len - 1; i++) {
        let point = {}
        let point1 = controlPoints[i]
        let point2 = controlPoints[i + 1] || controlPoints[0]
        point.x = (point1.x + point2.x) / 2
        point.y = (point1.y + point2.y) / 2
        result.push(point)
      }
      return result
    },
    updateControlPoints (controlPoints, offset = 0) {
      let result = deepCopy(controlPoints)
      let _min = Math.max(this.baseData.Rmin - offset, 0)
      let _max = Math.max(this.baseData.Rmax - offset, 0)
      result.forEach(v => {
        let Vline = Math.random() > 0.5 ? this.baseData.Vline : -this.baseData.Vline
        if (v.r >= _max) Vline = -this.baseData.Vline
        else if (v.r <= _min) Vline = this.baseData.Vline
        v.r = Math.min(Math.max(v.r + Vline, _min), _max)
        v.angle = (v.angle + this.baseData.Vangle) % (Math.PI * 2)
        v.x = this.baseData.CenterX + (Math.cos(v.angle) * v.r)
        v.y = this.baseData.CenterY + (Math.sin(v.angle) * v.r)
      })
      return result
    },
    drawItem (item, k) {
      this.circleList[k].controlPoints = this.updateControlPoints(item.controlPoints, item.offset)
      this.circleList[k].points = this.createPointsByControlPoints(this.circleList[k].controlPoints)
      item = this.circleList[k]
      this.ctx.strokeStyle = (k === 'main' ? this.mainColor : this.auxColor)
      this.ctx.lineWidth = 1
      this.ctx.lineJoin = 'round'
      this.ctx.lineCap = 'round'
      this.ctx.beginPath()
      let _len = item.controlPoints.length
      let _points = [item.points[_len - 1], ...item.points]
      for (let i = 0; i< _len; i++) {
        if (i === 0) this.ctx.moveTo(_points[i].x, _points[i].y)
        this.ctx.quadraticCurveTo(
          item.controlPoints[i].x, item.controlPoints[i].y,
          _points[i + 1].x, _points[i + 1].y
        )
      }
      this.ctx.stroke()
      this.ctx.closePath()
      if (k === 'main') {
        this.ctx.clip()
        this.imgObject && this.ctx.drawImage(this.imgObject, 0, 0, this.width, this.height)
        this.ctx.fillStyle = this.maskColor
        this.ctx.fill()
      }
    },
    draw () {
      this.ctx.restore()
      this.ctx.clearRect(0, 0, this.width, this.height)
      this.ctx.save()
      this.circleListKeys.forEach(k => this.drawItem(this.circleList[k], k))
      // window.requestAnimationFrame(this.draw)
      setTimeout(this.draw, this.interval)
    },
    loadImage (imageUrl) {
      this.imgObject = null
      if (!imageUrl) return
      let img = new Image()
      img.onload = () => {
        this.imgObject = img
      }
      img.setAttribute('crossOrigin', 'anonymous')
      img.src = imageUrl || ''
    }
  }
}
</script>
